/*
    i2cbusses: Print the installed i2c busses.
               Part of user-space programs to access for I2C
               devices.
    Copyright (c) 1999-2003  Frodo Looijaard <frodol@dds.nl> and
                             Mark D. Studebaker <mdsxyz123@yahoo.com>
    Copyright (C) 2008-2012  Jean Delvare <khali@linux-fr.org>
	Copyright (C) 2025  Luohao <luohao@100trust.cn>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
    MA 02110-1301 USA.
*/

/* For strdup and snprintf */
#define _DEFAULT_SOURCE 1
#define MDBCTL "/opt/bmc/skynet/lua /opt/bmc/apps/mdbctl/service/mdbctl.lua"

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>	/* for NAME_MAX */
#include <sys/ioctl.h>
#include <string.h>
#include <strings.h>	/* for strcasecmp() */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include "i2cbusses.h"
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <stdarg.h>

enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };

bool DEBUG_MODE = false;

void debug_print(const char *format, ...) 
{
    if (DEBUG_MODE) {
        fprintf(stderr, "[DEBUG] "); // 打印调试前缀
        va_list args;
        va_start(args, format);     // 初始化不定长参数列表
        vfprintf(stderr, format, args); // 打印格式化内容
        va_end(args);               // 清理参数列表
        fprintf(stderr, "\n");       // 换行（可选）
    }
}

static int lookup_i2c_bus_by_name(const char *bus_name)
{
	// 支持i2c-x形式的总线名称
	int i2cbus = -1;
	if (sscanf(bus_name, "i2c-%d", &i2cbus) >= 1){
		if (i2cbus < 0 || i2cbus > 255) {
			i2cbus = -1;
    	}
	}

	if (i2cbus == -1)
		fprintf(stderr, "Error: I2C bus name doesn't match any "
			"bus present!\n");
	return i2cbus;
}

/*
 * Parse an I2CBUS command line argument and return the corresponding
 * bus number, or a negative value if the bus is invalid.
 */
int lookup_i2c_bus(const char *i2cbus_arg)
{
	unsigned long i2cbus;
	char *end;

	i2cbus = strtoul(i2cbus_arg, &end, 0);
	if (*end || !*i2cbus_arg) {
		/* Not a number, maybe a name? */
		return lookup_i2c_bus_by_name(i2cbus_arg);
	}
	if (i2cbus > 0xFFFFF) {
		fprintf(stderr, "Error: I2C bus out of range!\n");
		return -2;
	}

	return i2cbus;
}

/*
 * Parse a CHIP-ADDRESS command line argument and return the corresponding
 * chip address, or a negative value if the address is invalid.
 */
int parse_i2c_address(const char *address_arg)
{
	long address;
	char *end;

	address = strtol(address_arg, &end, 0);
	if (*end || !*address_arg) {
		fprintf(stderr, "Error: Chip address is not a number!\n");
		return -1;
	}
	if (address < 0x03 || address > 0x77) {
		fprintf(stderr, "Error: Chip address out of range "
			"(0x03-0x77)!\n");
		return -2;
	}

	return address;
}

int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
{
	int file;

	snprintf(filename, size, "/dev/i2c/%d", i2cbus);
	filename[size - 1] = '\0';
	file = open(filename, O_RDWR);

	if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
		sprintf(filename, "/dev/i2c-%d", i2cbus);
		file = open(filename, O_RDWR);
	}

	if (file < 0 && !quiet) {
		if (errno == ENOENT) {
			fprintf(stderr, "Error: Could not open file "
				"`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
				i2cbus, i2cbus, strerror(ENOENT));
		} else {
			fprintf(stderr, "Error: Could not open file "
				"`%s': %s\n", filename, strerror(errno));
			if (errno == EACCES)
				fprintf(stderr, "Run as root?\n");
		}
	}

	return file;
}

int set_slave_addr(int file, int address, int force)
{
	/* With force, let the user read from/write to the registers
	   even when a driver is also running */
	if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
		fprintf(stderr,
			"Error: Could not set address to 0x%02x: %s\n",
			address, strerror(errno));
		return -errno;
	}

	return 0;
}

/*
 * Parse a CHIP-ADDRESS command line argument and return the corresponding
 * chip address, or a negative value if the address is invalid.
 */
int i2c_parse_i2c_address(const char *address_arg)
{
	long address;
	char *end;

	address = strtol(address_arg, &end, 0);
	if (*end || !*address_arg) {
		fprintf(stderr, "Error: Chip address is not a number!\n");
		return -1;
	}
	if (address < 0x03 || address > 0x77) {
		fprintf(stderr, "Error: Chip address out of range "
			"(0x03-0x77)!\n");
		return -2;
	}

	return address;
}

int mdbctl_smbus_read_byte(int i2cbus, int addr)
{
	FILE *fp;
    char cmd[1024];  /* Increased buffer size to avoid truncation */

    // 执行命令并获取输出
	snprintf(cmd, sizeof(cmd), "%s call I2c_%d bmc.kepler.Bus.BlockIO Read 0 0x%02x 1", MDBCTL, i2cbus, addr*2+1);
	debug_print("cmd: %s", cmd);
    fp = popen(cmd, "r");
    if (fp == NULL) {
        perror("popen failed");
        return 1;
    }

    // 读取第一行
    char line[1024];
    if (!fgets(line, sizeof(line), fp)) {
        perror("popen result is empty or read error.\n");
        pclose(fp);
        return 1;
    }
	debug_print("cmd return: %s", line);

    // 查找关键字
	int num = -1;
	if (sscanf(line, "[%d]", &num) >= 1){
		if (num >= 0 && num <= 255) {
			return 0;
    	}
	}

    // 关闭管道
    pclose(fp);
    return 1;
}

int busctl_i2c_read(int i2cbus, int addr, unsigned short len, unsigned char *buf)
{
	FILE *fp;
    char cmd[1024];  /* Increased buffer size to avoid truncation */

    // 执行命令并获取输出
	snprintf(cmd, sizeof(cmd), "%s call I2c_%d bmc.kepler.Bus.BlockIO Read 0 0x%02x 0x%02x", MDBCTL, i2cbus, addr*2+1, len);
	debug_print("cmd: %s", cmd);
    fp = popen(cmd, "r");
    if (fp == NULL) {
        perror("popen failed");
        return 1;
    }

    // 读取第一行
    char line[1024];
    if (!fgets(line, sizeof(line), fp)) {
        perror("popen result is empty or read error.\n");
        pclose(fp);
        return 1;
    }
	pclose(fp);
	debug_print("cmd return: %s", line);

	if (strncmp(line, "[", 1)) {
		fprintf(stderr, "return data error: %s\n", line);
		return 1;
	}

	// 去掉首尾的 '[' 和 ']'
	char *str = strdup(line + 1); // 跳过 '['
	char *pos = strchr(str, ']');
    if (pos != NULL) {
        *pos = '\0';
    }   // 去掉 ']'
	debug_print("return data: %s\n", str);

	// 用 strtok 分割逗号
	char *token = strtok(str, ",");
	unsigned short buffer_index = 0;
	while (token != NULL) {
		debug_print("token: %s\n", token);
		if (buffer_index < len){
		buf[buffer_index++] = (unsigned char)atoi(token);
		token = strtok(NULL, ",");
		}
		else{
			fprintf(stderr, "return data length is over %d.\n", len);
			return 1;
		}
	}
	free(str); // 释放临时字符串

	if(buffer_index != len){
		fprintf(stderr, "return data length is not %d.\n", len);
		return 1;
	}

    return 0;
}

int busctl_i2c_write(int i2cbus, int addr, unsigned short len, unsigned char *buf)
{
	FILE *fp;
    char cmd[1024];  /* Increased buffer size to avoid truncation */
	char hex_str[512] = ""; // 存储16进制字符串

    for (int i = 0; i < len; i++) {
		debug_print("buf[%d]: 0x%02X", i, buf[i]);
        char hex_byte[6];
        snprintf(hex_byte, sizeof(hex_byte), "0x%02X ", buf[i]);
        strcat(hex_str, hex_byte);
    }
	debug_print("write data: %s", hex_str);

    // 执行命令并获取输出
	snprintf(cmd, sizeof(cmd), "%s call I2c_%d bmc.kepler.Bus.BlockIO Write 0 0x%x %d %s", MDBCTL, i2cbus, addr*2, len, hex_str);
	debug_print("cmd: %s", cmd);
    fp = popen(cmd, "r");
    if (fp == NULL) {
        perror("popen failed");
        return 1;
    }

    // 读取第一行
    char line[1024];
    if (!fgets(line, sizeof(line), fp)) {
        perror("popen result is empty or read error.\n");
        pclose(fp);
        return 1;
    }
	debug_print("cmd return: %s", line);

	if(strncmp(line, "\n", 1)){
		fprintf(stderr, "write data to i2c bus %d address 0x%02x failed. error: %s\n", i2cbus, addr, line);
		pclose(fp);
		return 1;
	}
	
    // 关闭管道
    pclose(fp);
    return 0;
}

int busctl_i2c_write_read(int i2cbus, int addr, unsigned short write_len, unsigned char *write_buf, unsigned short read_len, unsigned char *read_buf)
{
	FILE *fp;
    char cmd[1024];  /* Increased buffer size to avoid truncation */
	char hex_str[512] = ""; // 存储16进制字符串

    for (unsigned short i = 0; i < write_len; i++) {
		debug_print("buf[%lu]: 0x%02X", i, write_buf[i]);
        char hex_byte[6];
        snprintf(hex_byte, sizeof(hex_byte), "0x%02X ", write_buf[i]);
        strcat(hex_str, hex_byte);
    }
	debug_print("write data: %s", hex_str);

    // 执行命令并获取输出
	snprintf(cmd, sizeof(cmd), "%s call I2c_%d bmc.kepler.Bus.BlockIO WriteRead 0 0x%x %u %s %u", MDBCTL, i2cbus, addr*2, write_len, hex_str, read_len);
	debug_print("cmd: %s", cmd);
    fp = popen(cmd, "r");
    if (fp == NULL) {
        perror("popen failed");
        return 1;
    }

    // 读取第一行
    char line[1024];
    if (!fgets(line, sizeof(line), fp)) {
        perror("popen result is empty or read error.\n");
        pclose(fp);
        return 1;
    }
	pclose(fp);
	debug_print("cmd return: %s", line);

	if (strncmp(line, "[", 1)) {
		fprintf(stderr, "return data error: %s\n", line);
		return 1;
	}

	// 去掉首尾的 '[' 和 ']'
	char *str = strdup(line + 1); // 跳过 '['
	char *pos = strchr(str, ']');
    if (pos != NULL) {
        *pos = '\0';
    }   // 去掉 ']'
	debug_print("return data: %s\n", str);

	// 用 strtok 分割逗号
	char *token = strtok(str, ",");
	unsigned short buffer_index = 0;
	while (token != NULL) {
		debug_print("token: %s\n", token);
		if (buffer_index < read_len){
		read_buf[buffer_index++] = (unsigned char)atoi(token);
		token = strtok(NULL, ",");
		}
		else{
			fprintf(stderr, "return data length is over %u.\n", read_len);
			return 1;
		}
	}
	free(str); // 释放临时字符串

	if(buffer_index != read_len){
		fprintf(stderr, "return data length is not %u.\n", read_len);
		return 1;
	}

    return 0;
}

/*
 * Print the installed i2c busses. 
 */
int print_i2c_busses(void)
{
	FILE *fp;
	char cmd[1024];  /* Increased buffer size to avoid truncation */
    char buffer[1024];

    // 执行命令并获取输出
	snprintf(cmd, sizeof(cmd), "%s lsobj I2c", MDBCTL);
	debug_print("cmd: %s", cmd);
    fp = popen(cmd, "r");
    if (fp == NULL) {
        perror("popen failed");
        return 1;
    }

    // 逐行读取命令输出
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
		char num_str[3]; // 存储数字部分的字符串
        char remaining[1024]; // 存储剩余部分
        if (sscanf(buffer, "I2c_%2[0-9]%s", num_str, remaining) == 2) {
            continue;
        } else if (sscanf(buffer, "I2c_%2[0-9]", num_str) == 1) {
            printf("i2c-%-3s   i2c             HIS i2c bit bus                         I2C adapter\n", num_str);
        }
    }

    // 关闭管道
    pclose(fp);
    return 0;
}
